Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create ms_server_denial_of_service.py #10

Merged
merged 6 commits into from
Feb 23, 2017
Merged

Conversation

vah13
Copy link
Contributor

@vah13 vah13 commented Feb 15, 2017

Hi.
We found a DoS vulnerability in Message Server (all kernel versions)

dos

May can you add functionality for SAP route in send_crash function?
I requested CVE and think will get for 24 hours.

@vah13
Copy link
Contributor Author

vah13 commented Feb 15, 2017

I used requests :-) sorry for that )

@vah13 vah13 closed this Feb 15, 2017
@vah13 vah13 reopened this Feb 15, 2017
@martingalloar
Copy link
Collaborator

Hi! Thanks, this is great as always!

As this is in the HTTP interface, I don't see a fit for having it available on pysap more than for using it through a SAPRouter. Even in that case that can be easily done using router_portfw.py, but it might be worth a try.

Can you make this work using plain sockets instead of requests? If so you can make it use the SAPRoutedStreamSocket and have it routing the traffic as in https://github.com/CoreSecurity/pysap/blob/master/examples/enqueue_dos_exploit.py.

@vah13
Copy link
Contributor Author

vah13 commented Feb 16, 2017

Hi Martin,
We have a question.

We changed script but in testing time got HTTP request, from SAP Router, with package length like this
00000008GET /ABC

can you say, how we can fix it (to router don't send packages with length of data)?

thank you

@martingalloar
Copy link
Collaborator

In order to the stream socket not append the NI layer you should use the NI_RAW_IO talk mode, like doing:

conn = SAPRoutedStreamSocket.get_nisocket(host, port, route, talk_mode=1)

@gelim
Copy link
Contributor

gelim commented Feb 17, 2017

Hi Martin,
yes router_portfw.py method is working fine, now trying to achieve the same result within the exploit via SAPRoutedStreamSocket with your mention is failing. Consider following POC to showcase that talk_mode doesn't seems to be taken into account:

from pysap.SAPRouter import SAPRoutedStreamSocket

route = '/H/yoursaprouter/S/3299'
host = 'desthttpmsgserver'
port = '8101'
conn = SAPRoutedStreamSocket.get_nisocket(host, port, route, talk_mode=1)
conn.send('ABCD')
conn.close()

That will spit the following packet from the 'yoursaprouter' host:

13:18:45.002289 IP yoursaprouter.52658 > desthttpmsgserver.8101: P 1:9(8) ack 1 win 115 <nop,nop,timestamp 2180275605 16712324>
        0x0000:  000c 29a7 eeb7 000c 29eb 8e69 0800 4500  ..).....)..i..E.
        0x0010:  003c 9ebb 4000 4006 2341 xxxx xxxx xxxx  .<..@.@.#A......
        0x0020:  xxxx xxxx 1fa5 ab5e c32e 6be6 6f85 8018  .......^..k.o...
        0x0030:  0073 78ee 0000 0101 080a 81f4 5d95 00ff  .sx.........]...
        0x0040:  0284 0000 0004 4142 4344                 ......ABCD

As you can see the NI layer is still used (with length being prepended)

@gelim
Copy link
Contributor

gelim commented Feb 17, 2017

I've got finally a working POC with:

router = yoursaprouter
dest = desthttpmsgserver
destport = '8101'

router_sock = SAPNIStreamSocket.get_nisocket(router, 3299, keep_alive=True)

# Route description to our destination
router_string = [SAPRouterRouteHop(hostname=router, port=3299),
                 SAPRouterRouteHop(hostname=dest, port=destport)]

# Forge SAPRouter packet to enable the route
router_string_lens = list(map(len, list(map(str, router_string))))
p = SAPRouter(type=SAPRouter.SAPROUTER_ROUTE,
              route_entries=len(router_string),
              route_talk_mode=1,
              route_rest_nodes=1,
              route_length=sum(router_string_lens),
              route_offset=router_string_lens[0],
              route_string=router_string)
resp = router_sock.sr(p)

# And now send our raw TCP payload to final destination
router_sock.ins.send("ABCD")

The important detail seems to be using the ins (or outs) members of SimpleSocket object for bypassing NI layer.

NB: My previous example after doing a pull didn't work anymore by spitting this:

DEBUG:pysap.sapni:To send 24 bytes
DEBUG:pysap.sapni:To receive 222 bytes
DEBUG:pysap.sapni:Received 222 bytes
DEBUG:pysap.saprouter:Requesting route to desthttpmsgserver:8101 using mode 1 (NI_RAW_IO)
DEBUG:pysap.sapni:To send 66 bytes
Traceback (most recent call last):
  File "./pysap_streamsocket_debug.py", line 19, in <module>
    conn = SAPRoutedStreamSocket.get_nisocket(host, port, route, talk_mode=1)
  File "build/bdist.linux-x86_64/egg/pysap/SAPRouter.py", line 673, in get_nisocket
  File "build/bdist.linux-x86_64/egg/pysap/SAPRouter.py", line 551, in __init__
  File "build/bdist.linux-x86_64/egg/pysap/SAPRouter.py", line 584, in route_to
  File "build/bdist.linux-x86_64/egg/pysap/SAPNI.py", line 162, in sr
  File "build/bdist.linux-x86_64/egg/pysap/SAPRouter.py", line 614, in recv
  File "build/bdist.linux-x86_64/egg/pysap/SAPNI.py", line 122, in recv
socket.error: (100, 'Underlying stream socket tore down')

@gelim
Copy link
Contributor

gelim commented Feb 17, 2017

About my last remark, I pointed out that get_router_version() is not working as supposed.
By forcing router_version like: conn = SAPRoutedStreamSocket.get_nisocket(host, port, route, talk_mode=1, router_version=40) will fix the situation, so that you can investigate why talk_mode is not taken into account.

@martingalloar
Copy link
Collaborator

martingalloar commented Feb 17, 2017

About my last point, I pointed out that get_router_version() is not working as supposed.
By forcing router_version like: conn = SAPRoutedStreamSocket.get_nisocket(host, port, route, talk_mode=1, router_version=40) will fix the situation, so that you can investigate why talk_mode is not taken into account.

Yes, I broke it in d2f0424! Fixing right now!

martingalloar added a commit that referenced this pull request Feb 17, 2017
Even when control text length is 0, the packet contains the eyecatcher string ("*ERR"). Pointed out by Mathieu Geli in #10! Thanks!
martingalloar added a commit that referenced this pull request Feb 17, 2017
Raw talk mode was only implemented for receiving packets. Pointed out by Mathieu Geli in #10! Thanks!
@martingalloar
Copy link
Collaborator

Check it out and let me know how it goes now! Missed out that send method was not even imlpemented in SAPRoutedStreamSocket and thus adding the NI layer even when talk mode was set to raw.

@gelim
Copy link
Contributor

gelim commented Feb 17, 2017

yeah that fixes everything, and simplifies the Router part of the POC as you advised ins issuecomment-280381063
👍

@martingalloar
Copy link
Collaborator

Thanks man! Will check and merge ASAP!

@martingalloar martingalloar self-assigned this Feb 17, 2017
@martingalloar
Copy link
Collaborator

Find out that this approach doesn't work for non routed connections, as the NI layer is still added. I'm changing the behaviour of SAPRoutedStreamSocket.get_ni_socket to support this scenario.

@martingalloar
Copy link
Collaborator

Can you check it's working fine without routing on the msdos branch?

@gelim
Copy link
Contributor

gelim commented Feb 17, 2017

yes, without a route, I don't see the length field in the payload sent to the destination. c33e720 seems to work properly.

... ok with further testing I see incompatibilities:

  1. whith talk_mode=1 when route=None and you specify a base_cls on the get_nisocket() call: base_cls is passed via kwargs to StreamSocket that is not happy about it
  2. when doing a SAPRoutedStreamSocket.recv() in this special case, we don't have basecls field set in our StreamSocket, so again crash when StreamSocket.recv() is doing a pkt = self.basecls(pkt)

Attempt to fix that issue (but hard to know what will be the consequences in others scenarios):

diff --git a/pysap/SAPRouter.py b/pysap/SAPRouter.py
index 45e97fe..c492426 100644
--- a/pysap/SAPRouter.py
+++ b/pysap/SAPRouter.py
@@ -22,7 +22,7 @@ import re
 import logging
 from socket import error as SocketError
 # External imports
-from scapy.layers.inet import TCP
+from scapy.layers.inet import TCP, Raw
 from scapy.packet import Packet, bind_layers
 from scapy.supersocket import socket, StreamSocket
 from scapy.fields import (ByteField, ShortField, ConditionalField, StrField,
@@ -608,6 +608,7 @@ class SAPRoutedStreamSocket(SAPNIStreamSocket):
         # need the NI layer anymore. Just use the plain socket inside the
         # NIStreamSockets.
         if self.routed and self.talk_mode == 1:
+            if not self.base_cls: self.basecls = Raw
             return StreamSocket.recv(self)
         # If the route was not accepted yet or we're working on non-native talk
         # mode, we need the NI layer.
@@ -675,6 +676,7 @@ class SAPRoutedStreamSocket(SAPNIStreamSocket):
             # NI layer completely
             if talk_mode == 1:
                 sock = socket.create_connection((host, port))
+                if 'base_cls' in kwargs: kwargs.pop('base_cls')
                 return StreamSocket(sock, **kwargs)
 
             # Otherwise use the standard SAPNIStreamSocket get_nisocket method

martingalloar added a commit that referenced this pull request Feb 20, 2017
This ensures that calls to send/recv doesn't fail when no route was provided. Pointed out by @gelim in #10.
@martingalloar
Copy link
Collaborator

I made my try by adapting your patch a little bit: if instead of pop'ing the base class we force it to be Raw we don't need to check it in send/recv methods.

@martingalloar martingalloar merged commit 8da4eb5 into OWASP:master Feb 23, 2017
@martingalloar
Copy link
Collaborator

Merged in ab30711, thanks guys!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants